Docker Dockerfile

Docker

C'est quoi Docker ?

Comment l'installer ?

Un premier exemple

Un deuxième exemple avec Node.js

Les Dockerfiles

 

Docker

C'est quoi Docker ?

Docker est une plateforme qui va vous permettre d'exécuter votre code à l'intérieur d'un conteneur indépendamment de la machine sur laquelle vous êtes ! Un conteneur ressemble à une machine virtuelle sauf qu'il n'embarque pas tout un système d'exploitation avec lui ce qui lui permet de s'exécuter en quelque secondes et d'être beaucoup plus léger.

Docker peut donc résoudre notre problème d'environnement, car quelle que soit la machine que nous utiliserons, le code s'exécutera de la même manière.

La plateforme Docker est composée de deux éléments :

Si vous voulez plus d'informations sur le fonctionnement interne de Docker je vous redirige vers l'article du site officiel : Understanding Docker

Comment l'installer ?

Le client Docker fonctionne sur tous les systèmes d'exploitation. En revanche, le démon Docker utilise des fonctionnalités du noyau Linux afin de gérer les conteneurs. Il ne fonctionne donc que sur Linux. Heureusement la majorité des serveurs utilise Linux, et pour vos ordinateurs sous OS X, ou même Windows, il existe une solution.

Si vous utilisez une machine avec une distribution Linux vous pourrez lancer le démon Docker directement sur cette dernière, par contre avec Windows ou OS X vous devrez lancer le démon dans une machine virtuelle, mais rassurez-vous, c'est très simple !

Pour le guide d'installation de Docker en fonction de votre système je vous redirige de nouveau vers le site officiel : Guide d'installation

Un premier exemple

Avant de commencer, vous allez devoir télécharger une image Docker qui servira de base à vos prochains conteneurs.

Pour cet exemple, on va partir d'une image Ubuntu :

$ docker pull ubuntu:trusty

trusty: Pulling from ubuntu

e9e06b06e14c: Pull complete

a82efea989f9: Pull complete

37bea4ee0c81: Pull complete

07f8e8c5e660: Already exists

ubuntu:trusty: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.

Digest: sha256:014fa1d5b72b4fe0ec2b4642610fbbfdd52f502da8e14e80de07bd1dd774e4ef

Status: Downloaded newer image for ubuntu:trusty

Cette commande va télécharger depuis le Docker Hub l'image de la version 14.04 (trusty) d'Ubuntu. Il existe bien d'autres images que vous pourrez trouver sur le registry Docker.

Pour voir les images que vous avez téléchargées, utilisez cette commande :

$ docker images

REPOSITORY    TAG       IMAGE ID        CREATED       VIRTUAL SIZE

ubuntu        trusty    07f8e8c5e660    4 weeks ago   188.3 MB

Sur ma machine, l'image d'Ubuntu fais 188.3 MB, je vous avais dit que c'était léger en comparaison à une machine virtuelle !

Maintenant, nous allons lancer un conteneur et rentrer à l'intérieur :

$ docker run -it ubuntu:trusty bash

root@2cdceb5ff771:/#

Cette commande crée un conteneur à partir de l'image ubuntu:trusty, y lance le programme bash et y attache votre shell grâce aux options -it

Vous pouvez maintenant exécuter les commandes que vous voulez, elle s'exécuteront à l'intérieur du conteneur, par exemple :

root@2cdceb5ff771:/#

$ apt-get moo

                 (__)

                 (oo)

           /------\/

          / |    ||

         *  /\---/\

            ~~   ~~

..."Have you mooed today?"...

Vous pouvez quitter le conteneur en faisant un Ctrl-d

Maintenant que vous êtes retourné sur votre machine, vous pouvez afficher la liste des conteneurs lancés avec cette commande :

$ docker ps

CONTAINER ID    IMAGE   COMMAND   CREATED   STATUS    PORTS   NAMES

Il n'y a rien ? C'est normal ! En quittant le conteneur ce dernier s'est arrêté aussi. Pour l'afficher quand même, il suffit d'entrer cette commande :

$ docker ps -a

CONTAINER ID    IMAGE           COMMAND   CREATED         STATUS                    PORTS   NAMES

2cdceb5ff771    ubuntu:trusty   "bash"    12 minutes ago  Exited (0) 2 minutes ago          loving_newton

Et pour supprimer ce conteneur ?

$ docker rm 2cdc

2cdc

Évidemment, remplacez '2cdc' par le CONTAINER ID approprié.

Passons maintenant à un deuxième exemple plus concret avec une application web.

Un deuxième exemple avec Node.js

Pour commencer, vous allez récupérer l'image docker officiel de Node.js en faisant :

$ docker pull node:0.12.4

0.12.4: Pulling from node

7711db4bb553: Pull complete

d1744e6e9471: Pull complete

9332645b03a3: Pull complete

a52a290821b3: Pull complete

3575f1347ce7: Already exists

39bb80489af7: Already exists

df2a0347c9d0: Already exists

7a3871ba15f8: Already exists

a2703ed272d7: Already exists

c9e3effdd23a: Already exists

node:0.12.4: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.

Digest: sha256:81fb0812dd5e81f768773a121c8a6daced36893210c5ed50b504c4abcb04e10c

Status: Downloaded newer image for node:0.12.4

Puis créez un fichier server.js avec le contenu suivant :

var http = require("http");

 

var server = http.createServer(function(req, res) {

  res.end("Coucou depuis Docker");

});

 

server.listen(3000);

Et maintenant, pour lancer notre application à l'intérieur d'un conteneur, vous devez faire :

$ docker run -d --name node-app -p 3000:3000 -v $(pwd):/app node:0.12.4 node /app/server.js

e9ca3cd8f90b8554ca99ec8ba15a039f827005bd8fecbf80d72ce7267006a6df

Si vous vous rendez sur localhost:3000 (ou l'IP de la VM si êtes sur Windows ou Mac), vous verrez : 'Coucou depuis Docker'

C'est beau, mais comment ça marche ? Examinons les options une par une :

Et maintenant ? Vous pouvez afficher le conteneur en faisant : docker ps, l'arrêter avec : docker stop node-app et le supprimer avec docker rm node-app.

Les Dockerfiles

Les Dockerfiles sont des fichiers qui permettent de construire une image Docker adaptée à nos besoins, étape par étape. Rentrons dans le vif du sujet en créant une image permettant de lancer un projet JavaScript.

Pour commencer, créez un nouveau fichier Dockerfile à la racine de votre projet.

La première chose à faire dans un Dockerfile est de définir de quelle image vous héritez. Pour cet exemple, je vous propose d'utiliser une image de Debian comme base (ce qui est une bonne pratique, car cette image est plutôt légère en comparaison avec celle d'Ubuntu par exemple).

FROM debian:jessie

FROM permet de définir notre image de base, vous pouvez l'utiliser seulement une fois dans un Dockerfile.

Comme nous voulons créer une image pour une application JavaScript full-stack, nous devons commencer par installer Node.js. Pour ce faire, on va télécharger l'archive Node.js directement depuis le site officiel à l'aide de curl que nous allons aussi devoir installer.

RUN apt-get update \

&& apt-get install -y curl \

&& rm -rf /var/lib/apt/lists/*

 

RUN curl -LO "https://nodejs.org/dist/v0.12.5/node-v0.12.5-linux-x64.tar.gz" \

&& tar -xzf node-v0.12.5-linux-x64.tar.gz -C /usr/local --strip-components=1 \

&& rm node-v0.12.5-linux-x64.tar.gz

RUN permet d'exécuter une commande à l'intérieur de votre image comme si vous étiez devant un shell unix.

La première commande nous permet d'installer curl et de nettoyer ensuite le gestionnaire de paquets afin que notre image soit un peu plus légère.

Avec la deuxième commande, nous téléchargeons le binaire de Node.js que nous installons ensuite à sa place, et on n'oublie pas de supprimer l'archive ensuite.

Vous pouvez vous demander pourquoi j'exécute plusieurs commandes sur une même instruction RUN ? Eh bien, cela permet simplement de limiter le nombre d'instructions dans votre Dockerfile ce qui rendra votre image finale plus légère.

Maintenant, nous allons ajouter les sources de notre projet dans l'image et télécharger nos dépendances.

ADD package.json /app/

 

WORKDIR /app

 

RUN npm install

 

ADD . /app/

ADD permet d'ajouter des fichiers locaux ou distants à l'intérieur de votre image, il est le plus souvent utilisé pour importer les sources de votre projet ou des fichiers de configuration.

WORKDIR permet de changer le répertoire courant de votre image, toutes les commandes qui suivront seront exécutées à partir de ce répertoire.

Avec la dernière instruction, nous ajoutons les sources de notre projet à l'intérieur de l'image, mais vous allez vous demander pourquoi nous ne l'avons pas fait en même temps que l'ajout des fichiers de dépendances. Eh bien, cela nous permet d'économiser beaucoup de temps !

Quand Docker crée une nouvelle image à partir d'un Dockerfile, il exécute chaque instruction dans un conteneur, et le résultat de cette instruction est sauvegardé sous forme de couche. Au final, une image est un assemblage de plusieurs couches (une par instruction). Et donc, quand vous reconstruisez une image pour la seconde fois, les instructions qui n'impliquent pas de changements ne sont pas réexécutées, car la couche est récupérée depuis l'image précédente. Par contre, si l'instruction implique un changement quelconque, elle est réexécutée ainsi que toutes les instructions suivantes.

Dans notre cas, les sources auront tendance à beaucoup changer, et donc ne pas retélécharger les dépendances à chaque changement dans le code est un réel gain de temps !

Maintenant, nous allons indiquer quel port et dossier nous souhaitons partager avec l'extérieur du conteneur.

EXPOSE 3000

 

VOLUME /app/log

EXPOSE et VOLUME permettent respectivement d'indiquer quel port et quel dossier nous souhaitons partager.

Et pour finir, nous pouvons indiquer quelle instruction doit s'exécuter au lancement de votre conteneur grâce à l'instruction CMD.

CMD node server.js

Voici un résumé de notre Dockerfile :

# Image de base

FROM debian:jessie

 

# Installation de curl avec apt-get

RUN apt-get update \

&& apt-get install -y curl \

&& rm -rf /var/lib/apt/lists/*

 

# Installation de Node.js à partir du site officiel

RUN curl -LO "https://nodejs.org/dist/v0.12.5/node-v0.12.5-linux-x64.tar.gz" \

&& tar -xzf node-v0.12.5-linux-x64.tar.gz -C /usr/local --strip-components=1 \

&& rm node-v0.12.5-linux-x64.tar.gz

 

# Ajout du fichier de dépendances package.json

ADD package.json /app/

 

# Changement du repertoire courant

WORKDIR /app

 

# Installation des dépendances

RUN npm install

 

# Ajout des sources

ADD . /app/

 

# On expose le port 3000

EXPOSE 3000

 

# On partage un dossier de log

VOLUME /app/log

 

# On lance le serveur quand on démarre le conteneur

CMD node server.js

Avant de transformer ce Dockerfile en une image, vous devez créer un fichier de plus, le .dockerignore, ce fichier permet comme un .gitignore de ne pas inclure certain fichiers dans votre image Docker, et c'est très important afin d'éviter d'inclure les dépendances de votre projet dans votre image (node_modules dans notre cas) qui sont propres à votre système, mais pas au système du conteneur. Voici à quoi votre .dockerignore doit ressembler :

node_modules

.git

Pour transformer ce Dockerfile en une image Docker, vous devez utiliser cette commande :

$ docker build -t fullstack-js .

Sending build context to Docker daemon 4.381 MB

Sending build context to Docker daemon

Step 0 : FROM debian:jessie

 ---> bf84c1d84a8f

Step 1 : RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

 ---> Running in 93258459a279

...

 ---> 4fffcf3749a2

Removing intermediate container 93258459a279

Step 2 : RUN curl -LO "https://nodejs.org/dist/v0.12.5/node-v0.12.5-linux-x64.tar.gz" && tar -xzf node-v0.12.5-linux-x64.tar.gz -C /usr/local --strip-components=1 && rm node-v0.12.5-linux-x64.tar.gz

 ---> Running in a3a17d584bae

...

 ---> 4eaa62ace8de

Removing intermediate container a3a17d584bae

Step 3 : ADD *.json /app/

 ---> 1e8ffd7e10a8

Removing intermediate container 5db20e8b8ed2

Step 4 : WORKDIR /app

 ---> Running in 7b84b06642b1

 ---> 9c0e2287c34d

Removing intermediate container 7b84b06642b1

Step 5 : RUN npm install

 ---> Running in 0523df6e9aac

...

 ---> 6d7327ebee30

Removing intermediate container 0523df6e9aac

Step 6 : ADD . /app

 ---> 13bdbe70c6fa

Removing intermediate container 3c83d82c1d53

Step 7 : EXPOSE 3000

 ---> Running in 51e252173b12

 ---> 6c62eb1197e2

Removing intermediate container 51e252173b12

Step 8 : VOLUME /app/log

 ---> Running in 4af0bb73307b

 ---> 15b6190de473

Removing intermediate container 4af0bb73307b

Step 9 : CMD node server.js

 ---> Running in 9522c6b9bf95

 ---> aaf20fb25dac

Removing intermediate container 9522c6b9bf95

Successfully built aaf20fb25dac

L'option -t permet de nommer votre image docker, ce qui vous servira lorsque vous voudrez lancer votre conteneur. Et le . est le repertoire où se trouve le Dockerfile, dans notre cas le dossier courant.

Maintenant, vous pouvez lancer votre conteneur de cette manière :

$ docker run -d -p 3000:3000 -v $(pwd)/log:/app/log fullstack-js

Cette commande permet de lancer notre image en partageant le port et un dossier avec votre ordinateur, si vous voulez plus de détails sur le fonctionnement du client Docker, je vous invite à lire mon article précédent.

En cherchant sur Internet, vous pourrez trouver des images Docker pour tout et n'importe quoi, comme des images pour lancer Chrome dans un conteneur par exemple. Pour en savoir plus, je vous redirige vers le blog de Jessie Frazelle.